﻿using Engine.Graphics;
using Engine.Graphics.Shaders;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.DXGI;

namespace Engine.RenderableObjects
{
    public class QuadRenderWindow : IRenderableObject
    {
        #region Private Fields

        private Graphic engine;
        private PixelShader ps;
        private SamplerState samplerStateStage0;
        private ShaderBase shaderBase;
        private ShaderBytecode shaderByteCodePS;
        private ShaderBytecode shaderByteCodeVS;
        private VertexBufferBinding vertexBufferBinding;
        private Matrix viewProjection;
        private VertexShader vs;

        #endregion Private Fields

        #region Public Constructors

        public QuadRenderWindow(Graphic engine)
        {
            this.engine = engine;

            // Load the PostProcessing Shader
            this.shaderBase = new ShaderBase(this.engine.Device);
            this.shaderBase.LoadShader(this.engine.Device, ".\\Shaders\\PostProcessing.fx", "ps_main", "vs_main");

            this.vs = this.shaderBase.vs;
            this.ps = this.shaderBase.ps;

            this.samplerStateStage0 = new SamplerState(this.engine.Device, new SamplerStateDescription()
                {
                    Filter = Filter.Anisotropic,
                    AddressU = TextureAddressMode.Wrap,
                    AddressV = TextureAddressMode.Wrap,
                    AddressW = TextureAddressMode.Wrap,
                    BorderColor = Color4.White,
                    ComparisonFunction = Comparison.Always,
                    MaximumAnisotropy = 16,
                    MaximumLod = float.MaxValue,
                    MinimumLod = 0,
                    MipLodBias = 0
                });

            //BufferDescription constanBufferDescription = new BufferDescription()
            //{
            //    CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.None,
            //    Usage = ResourceUsage.Default,
            //    OptionFlags = ResourceOptionFlags.None,
            //    BindFlags = BindFlags.ConstantBuffer,
            //    SizeInBytes = 256 // Matrix.SizeInBytes
            //};

            this.viewProjection = Matrix.OrthoLH((float)this.engine.viewPort.Width, (float)this.engine.viewPort.Height, 1, -1);
            this.viewProjection.Transpose();
            this.vertexBufferBinding = new VertexBufferBinding(null, 0, 0);
        }

        #endregion Public Constructors

        #region Public Methods

        public void Dispose()
        {
            this.engine.RemoveRenderableObject(this);
            if (this.vs != null) vs.Dispose(); vs = null;
            if (this.ps != null) ps.Dispose(); ps = null;
            if (this.shaderByteCodePS != null) this.shaderByteCodePS.Dispose(); this.shaderByteCodePS = null;
            if (this.shaderByteCodeVS != null) this.shaderByteCodeVS.Dispose(); this.shaderByteCodeVS = null;
        }

        public void Render(int renderStep)
        {
            this.engine.BlendStateEnableAlpha(false);
            this.engine.DepthStateEnableZ(false);

            lock (this.engine.Device.ImmediateContext)
            {
                DeviceContext deviceContext = this.engine.Device.ImmediateContext;

                deviceContext.OutputMerger.SetTargets(this.engine.depthStencilView, this.engine.renderViewBackbuffer);
                deviceContext.InputAssembler.SetVertexBuffers(0, this.vertexBufferBinding);
                deviceContext.InputAssembler.SetIndexBuffer(null, Format.Unknown, 0);
                deviceContext.InputAssembler.InputLayout = null;// this.inputLayout;
                deviceContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleStrip;
                deviceContext.PixelShader.Set(this.ps);
                deviceContext.PixelShader.SetSampler(0, this.samplerStateStage0);
                deviceContext.PixelShader.SetShaderResource(0, this.engine.renderToTextureSRV);
                deviceContext.VertexShader.Set(this.vs);
                deviceContext.GeometryShader.Set(null);
                deviceContext.Draw(3, 0);

                deviceContext.PixelShader.SetShaderResource(0, null); // release shaderresource to write later to it.
                deviceContext.OutputMerger.SetTargets(this.engine.depthStencilView, this.engine.renderView);
            }
        }

        #endregion Public Methods
    }
}